package org.originit.factory.support;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.XmlUtil;
import org.originit.exception.BeansException;
import org.originit.factory.config.BeanDefinition;
import org.originit.factory.config.BeanReference;
import org.originit.factory.config.PropertyValue;
import org.originit.factory.config.PropertyValues;
import org.originit.factory.core.io.Resource;
import org.originit.factory.core.io.ResourceLoader;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import java.io.IOException;
import java.io.InputStream;

/**
 * @author xxc
 */
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
    public XmlBeanDefinitionReader(BeanDefinitionRegistry beanDefinitionRegistry) {
        super(beanDefinitionRegistry);
    }

    public XmlBeanDefinitionReader(BeanDefinitionRegistry beanDefinitionRegistry, ResourceLoader resourceLoader) {
        super(beanDefinitionRegistry, resourceLoader);
    }

    @Override
    public void loadBeanDefinitions(Resource resource) throws BeansException {
        Document document;
        try {
            document = XmlUtil.readXML(resource.getInputStream());
        } catch (IOException e) {
            throw new BeansException("配置文件找不到");
        }
        NodeList beanList = XmlUtil.getNodeListByXPath("//beans/bean", document);
        for (int i = 0; i < beanList.getLength(); i++) {
            Node item = beanList.item(i);
            String className = getAttr(item,"class");
            String beanName = getAttr(item,"name");
            Class<?> clz = null;
            try {
                clz = Class.forName(className);
            } catch (ClassNotFoundException e) {
                throw new BeansException("class:" + className + "找不到");
            }
            NodeList props = null;
            try {
                props = XmlUtil.getNodeListByXPath("//prop", item);
            } catch (Exception e) {
            }
            PropertyValues propertyValues = new PropertyValues();
            for (int j = 0; props != null && j <  props.getLength(); j++) {
                Node prop = props.item(j);
                String propName = getAttr(prop,"name");
                Object propValue = getAttr(prop,"value");
                String propRef = getAttr(prop,"ref");
                if (propValue != null && propRef != null) {
                    throw new BeansException("prop标签不能同时拥有value和ref属性");
                }
                if (propName == null) {
                    throw new BeansException("prop标签必须填写name属性");
                }
                if (propValue != null) {
                    propertyValues.addPropertyValue(new PropertyValue(propName,propValue));
                } else {
                    propertyValues.addPropertyValue(new PropertyValue(propName,new BeanReference(propRef)));
                }
            }
            getRegistry().registerBeanDefinition(beanName,new BeanDefinition(clz, propertyValues));
        }
    }

    private String getAttr(Node node,String attrName) {
        Assert.notNull(node);
        Node namedItem = node.getAttributes().getNamedItem(attrName);
        if (namedItem == null) {
            return null;
        }
        return namedItem.getTextContent();
    }

    @Override
    public void loadBeanDefinitions(Resource... resources) throws BeansException {
        for (int i = 0; i < resources.length; i++) {
            loadBeanDefinitions(resources[i]);
        }
    }

    @Override
    public void loadBeanDefinitions(String location) throws BeansException {
        Resource resource = getResourceLoader().getResource(location);
        loadBeanDefinitions(resource);
    }
}
